package com.zhdl.common.ytools;

import lombok.extern.slf4j.Slf4j;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class BitConversionTools {

    /**
     * 通过寄存器地址 读取寄存器的地址值
     *
     * @param hexData     十六进制全数据
     * @param hexAddress  十六进制地址
     * @param readableNum 需要读取的数量
     * @return 指定的个数寄存器值
     */
    public static String readHexValueByAddress(String hexData, String hexAddress, int readableNum) {
        // 确保hexData长度是偶数，否则可能不是有效的十六进制数据
        if (hexData.length() % 2 != 0) {
            throw new IllegalArgumentException("hexData must have an even length");
        }
        // 将起始地址从十六进制转换为十进制
        int decAddress = Integer.parseInt(hexAddress, 16);
        // 计算起始索引（因为每个寄存器是16位，即2个十六进制字符，所以乘以2）
//        int startIndex = decAddress * 2;
        int startIndex = decAddress * 4;
        // 计算结束索引（基于寄存器数量，每个寄存器2个字符）
//        int endIndex = startIndex + readableNum * 2;
        int endIndex = startIndex + readableNum * 4;
        // 确保索引在hexData的范围内
        if (startIndex < 0 || endIndex > hexData.length()) {
            throw new IndexOutOfBoundsException("Address or number of registers out of bounds");
        }
        // 提取子字符串
        String substring = hexData.substring(startIndex, endIndex);
//        log.debug("readHexValueByAddress --> {}", substring);
        return substring;
    }

    /**
     * 通过寄存器地址范围 读取寄存器的地址值
     *
     * @param hexData         十六进制全数据
     * @param hexStartAddress 十六进制的起始地址
     * @param hexEndAddress   十六进制的结束地址
     * @return 指定范围的寄存器值
     */
    public static String readHexValueByRange(String hexData, String hexStartAddress, String hexEndAddress) {
        // 确保hexData长度是偶数，否则可能不是有效的十六进制数据
        if (hexData.length() % 2 != 0) {
            throw new IllegalArgumentException("hexData must have an even length");
        }
        // 将起始地址和结束地址从十六进制转换为十进制
        int decStartAddress = Integer.parseInt(hexStartAddress, 16);
        int decEndAddress = Integer.parseInt(hexEndAddress, 16);
        // 计算起始索引（每个寄存器是16位，即2个十六进制字符，所以乘以2）
//        int startIndex = decStartAddress * 2;
        int startIndex = decStartAddress * 4;
        // 计算结束索引（同样乘以2，但注意结束索引是开区间，所以需要加2）
//        int endIndex = (decEndAddress + 1) * 2; // 加1是因为结束地址是下一个寄存器的起始地址
        int endIndex = (decEndAddress + 1) * 4; // 加1是因为结束地址是下一个寄存器的起始地址
        // 确保索引在hexData的范围内
        if (startIndex < 0 || endIndex > hexData.length() || startIndex >= endIndex) {
            throw new IndexOutOfBoundsException("Start or end address out of bounds or start >= end");
        }
        // 提取子字符串
        String substring = hexData.substring(startIndex, endIndex);
        log.debug("readHexValueByRange --> {}", substring);
        return substring;
    }

    /**
     * 通过十进制寄存器地址 读取寄存器的地址值
     *
     * @param hexData    十六进制全数据
     * @param decAddress 十进制地址
     * @return 指定的寄存器值
     */
    public static String readHexByDecAddress(String hexData, int decAddress) {
        // 确保hexData长度是偶数
        if (hexData.length() % 2 != 0) {
            throw new IllegalArgumentException("hexData must have an even length");
        }
        // 计算起始索引（每个寄存器两个字节，即四个十六进制字符）
        int startIndex = decAddress * 4; // 乘以4，因为每个字符代表一个十六进制位（4 bits）
        // 检查索引是否越界
        if (startIndex + 3 >= hexData.length()) { // 加3是因为我们需要4个字符
            throw new IndexOutOfBoundsException("decAddress out of bounds");
        }
        // 提取子字符串（四个字符）
        String substring = hexData.substring(startIndex, startIndex + 4);
//        log.debug("readHexByDecAddress start && num --> {}", substring);
        return substring;
    }

    /**
     * 通过十进制寄存器地址 读取寄存器的地址值
     *
     * @param hexData      十六进制全数据
     * @param startAddress 十进制的起始地址
     * @param endAddress   十进制的结束地址
     * @return 指定范围的寄存器值
     */
    public static String readHexByDecAddress(String hexData, int startAddress, int endAddress) {
        // 确保hexData长度是偶数
        if (hexData.length() % 2 != 0) {
            throw new IllegalArgumentException("hexData must have an even length");
        }
        // 计算起始和结束索引（每个寄存器两个字节，即四个十六进制字符）
        int startIndex = startAddress * 4;
        int endIndex = (endAddress + 1) * 4; // 加1是因为结束地址是下一个寄存器的起始地址
        // 检查索引是否越界
        if (startIndex < 0 || endIndex > hexData.length() || startIndex >= endIndex) {
            throw new IndexOutOfBoundsException("Start or end address out of bounds or start >= end");
        }
        // 提取子字符串
        String substring = hexData.substring(startIndex, endIndex);
//        log.debug("readHexByDecAddress start && end --> {}", substring);
        // 检查substring的长度是否等于(endAddress - startAddress + 1) * 4
        // 如果不等于，可能表示数据不完整或有误（但这不是必须的，取决于你的需求）
        return substring;
    }

    /**
     * 十六进制字符串转List<String>的Bit
     *
     * @param hexString 十六进制字符串
     * @return 传入寄存器的List<数组>
     */
    public static List<String> hexStrToBinaryStrings(String hexString) {
        List<String> binaryStrings = new ArrayList<>();
        // 去除字符串两端的空格和非法字符（如果需要）
        hexString = hexString.trim().replaceAll("[^0-9A-Fa-f]", "");
        // 每4个字符分割一次
        for (int i = 0; i < hexString.length(); i += 4) {
            if (i + 4 <= hexString.length()) {
                // 截取4个字符的十六进制子串
                String hexSubstring = hexString.substring(i, i + 4);
                // 转换为十进制整数
                int decimal = Integer.parseInt(hexSubstring, 16);
                // 转换为二进制字符串，并确保至少16位（如果需要）
                String binaryString = Integer.toBinaryString(decimal);
                // 如果不足16位，则在前面补零
                binaryString = String.format("%16s", binaryString).replace(' ', '0');
                // 将二进制字符串添加到列表中
                binaryStrings.add(binaryString);
            } else {
                // 如果最后一个寄存器不足4个字符，则忽略或抛出异常（根据需要）
                // 这里简单处理为忽略
                break;
            }
        }
        return binaryStrings;
    }

    /**
     * 十六进制字符串转List<String>反转后的的Bit
     *
     * @param hexString 十六进制字符串
     * @return 传入寄存器的List<数组>
     */
    public static List<String> hexStrToReversedBinaryStrings(String hexString) {
        List<String> binaryStrings = new ArrayList<>();
        // 去除字符串两端的空格和非法字符（如果需要）
        hexString = hexString.trim().replaceAll("[^0-9A-Fa-f]", "");
        // 每4个字符分割一次
        for (int i = 0; i < hexString.length(); i += 4) {
            if (i + 4 <= hexString.length()) {
                // 截取4个字符的十六进制子串
                String hexSubstring = hexString.substring(i, i + 4);
                // 转换为十进制整数
                int decimal = Integer.parseInt(hexSubstring, 16);
                // 转换为二进制字符串，并确保至少16位（如果需要）
                String binaryString = Integer.toBinaryString(decimal);
                // 如果不足16位，则在前面补零
                binaryString = String.format("%16s", binaryString).replace(' ', '0');
                // 使用StringBuilder来反转二进制字符串
                StringBuilder sb = new StringBuilder(binaryString);
                String reversedBinaryString = sb.toString();
                // 将反转后的二进制字符串添加到列表中
                binaryStrings.add(reversedBinaryString);
            } else {
                // 如果最后一个寄存器不足4个字符，则忽略或抛出异常（根据需要）
                // 这里简单处理为忽略
                break;
            }
        }
        return binaryStrings;
    }

    /**
     * 十六进制字符串转String类型的Bit
     *
     * @param hexStr 十六进制字符串
     * @return 二进制的位字符串
     */
    public static String HexStrConversionBit(String hexStr) {
        List<String> binaryStrings = hexStrToBinaryStrings(hexStr);
        StringBuilder binaryString = new StringBuilder();
        for (String bitString : binaryStrings) {
            binaryString.append(bitString);
        }
        return binaryString.toString();
    }

    /**
     * 十六进制字符串转String类型的每八位反转的Bit
     *
     * @param hexStr 十六进制字符串
     * @return 反转后的二进制的位字符串
     */
    public static String HexStrToReversalBit(String hexStr) {
        List<String> binaryStrings = hexStrToReversedBinaryStrings(hexStr);
        StringBuilder binaryString = new StringBuilder();
        for (String bitString : binaryStrings) {
            binaryString.append(bitString);
        }
        return binaryString.toString();
    }

    /**
     * 根据位下标 修改hex寄存器中的值
     *
     * @param hexData  十六进制数据
     * @param decIndex 十进制下标
     * @param newValue 修改的值
     * @return 修改后的十六进制
     */
    public static String yWriteValueByDecIndex(String hexData, int decIndex, char newValue) {
        if (hexData == null || hexData.isEmpty() || decIndex < 0) {
            throw new IllegalArgumentException("Invalid input for hexData or decIndex");
        }
        // 将十六进制字符串转换为反转的二进制字符串
        String bitArrayReversal = HexStrToReversalBit(hexData);
        // 修改二进制字符串中的特定位
        String modifiedBinaryString = writeBitValueByDecIndex(bitArrayReversal, decIndex, newValue);
        // 将二进制字符串转换回原始的（非反转的）二进制字符串
        StringBuilder originalBinary = new StringBuilder();
        for (int i = 0; i < modifiedBinaryString.length(); i++) {
            originalBinary.append(modifiedBinaryString.charAt(modifiedBinaryString.length() - 1 - i));
        }
        // 将二进制字符串转换为十六进制字符串
        return new BigInteger(originalBinary.toString(), 2).toString(16).toUpperCase();
    }

    public static String bWriteValueByDecIndex(String hexData, int decIndex, char newValue) {
        String bitArrayReversal = BitConversionTools.HexStrConversionBit(hexData);
        return modifyAndConvertBinaryToHex(bitArrayReversal, decIndex, newValue);
    }

    /**
     * 更改寄存器中，某一位的值
     *
     * @param binaryString 寄存器的位 字符串
     * @param bitIndex     字符串的下标 从0开始
     * @param newValue     要修改的值
     * @return 修改后的二进制字符串
     */
    public static String writeBitValueByDecIndex(String binaryString, int bitIndex, char newValue) {
        if (binaryString == null || binaryString.isEmpty() || bitIndex < 0 || bitIndex >= binaryString.length() || (newValue != '0' && newValue != '1')) {
            throw new IllegalArgumentException("Invalid input for binary string, bit index, or new value");
        }
        StringBuilder stringBuffer = new StringBuilder(binaryString);
        stringBuffer.setCharAt(bitIndex - 1, newValue);
        return stringBuffer.toString();
    }

    public static String writeBitValueByDecIndex1(String binaryString, int bitIndex, char newValue) {
        // 检查输入值是否有效
        if (binaryString == null || binaryString.isEmpty() || bitIndex < 0 || bitIndex >= binaryString.length() || (newValue != '0' && newValue != '1')) {
            throw new IllegalArgumentException("Invalid input for binary string, bit index, or new value");
        }
        // 创建StringBuilder以高效地修改字符串
        StringBuilder sb = new StringBuilder(binaryString);
        // 计算字符索引（因为字符串是字符的集合，而不是单独的位）
        int charIndex = bitIndex / 4; // 每个字符代表4个位（一个十六进制位）
        int bitOffset = bitIndex % 4; // 在字符内部的位偏移量
        // 创建一个掩码来更改特定位
        char mask = (char) (0xf << (4 - bitOffset)); // 创建一个掩码，除了要更改的位之外，其他位都是1
        char bitToSet = (char) ((newValue - '0') << bitOffset); // 创建一个只有要设置的位是1的字符
        // 清除原始位并设置新位
        char originalChar = sb.charAt(charIndex);
        sb.setCharAt(charIndex, (char) ((originalChar & ~mask) | bitToSet));
        // 返回修改后的字符串
        return sb.toString();
    }

    public static String modifyAndConvertBinaryToHex(String binaryString, int bitIndexToChange, char newValue) {
        // 检查输入是否有效
        if (binaryString == null || binaryString.isEmpty() || bitIndexToChange < 0 || bitIndexToChange >= binaryString.length() || (newValue != '0' && newValue != '1')) {
            throw new IllegalArgumentException("Invalid input for binary string, bit index, or new value");
        }

        // 1. 将二进制字符串转换为十六进制字符串（但这一步在此场景中不是必需的）
        // String hexString = new BigInteger(binaryString, 2).toString(16);

        // 2. 反转每个十六进制字符对应的二进制字符串
        StringBuilder reversedBinary = new StringBuilder();
        for (int i = 0; i < binaryString.length(); i += 8) {
            if (i + 8 <= binaryString.length()) {
                String hexCharBinary = binaryString.substring(i, i + 8);
                String reversedHexCharBinary = new StringBuilder(hexCharBinary).reverse().toString();
                reversedBinary.append(reversedHexCharBinary);
            } else {
                // 处理不足8位的二进制字符串（如果有的话）
                String hexCharBinary = binaryString.substring(i);
                String reversedHexCharBinary = new StringBuilder(hexCharBinary).reverse().toString();
                reversedBinary.append(reversedHexCharBinary);
            }
        }

        // 3. 修改反转后的二进制字符串中的某个位
        StringBuilder modifiedBinary = new StringBuilder(reversedBinary);
        int charIndex = bitIndexToChange / 8; // 计算字符索引
        int bitOffset = bitIndexToChange % 8; // 计算位偏移量
//        modifyBit1(modifiedBinary, charIndex, bitOffset, newValue);
        modifyBit(modifiedBinary, charIndex, newValue);

        // 4. 再次反转修改过的二进制字符串以恢复原顺序
        StringBuilder finalBinary = new StringBuilder();
        for (int i = 0; i < modifiedBinary.length(); i += 8) {
            if (i + 8 <= modifiedBinary.length()) {
                String hexCharBinary = modifiedBinary.substring(i, i + 8);
                String originalHexCharBinary = new StringBuilder(hexCharBinary).reverse().toString();
                finalBinary.append(originalHexCharBinary);
            } else {
                // 处理不足8位的二进制字符串（如果有的话）
                String hexCharBinary = modifiedBinary.substring(i);
                String originalHexCharBinary = new StringBuilder(hexCharBinary).reverse().toString();
                finalBinary.append(originalHexCharBinary);
            }
        }

        // 5. 将合并后的二进制字符串转换回十六进制字符串
        String finalHexString = new BigInteger(finalBinary.toString(), 2).toString(16).toUpperCase();
        // 去除前导零（如果需要）
        finalHexString = finalHexString.replaceAll("^0+(?!$)", "");

        return finalHexString;
    }

    private static void modifyBit(StringBuilder binary, int bitIndex, char newValue) {
        if (bitIndex < 0 || bitIndex >= binary.length()) {
            throw new IndexOutOfBoundsException("Bit index out of range");
        }
        if (newValue != '0' && newValue != '1') {
            throw new IllegalArgumentException("New value must be '0' or '1'");
        }
        // 直接设置指定索引处的字符
        binary.setCharAt(bitIndex, newValue);
    }

    private static void modifyBit1(StringBuilder binary, int charIndex, int bitOffset, char newValue) {
        char originalChar = binary.charAt(charIndex);
        char mask = (char) (0xff << (8 - bitOffset - 1) * 2); // 创建一个掩码，除了要更改的位之外，其他位都是1
        char bitToSet = (char) ((newValue - '0') << (8 - bitOffset - 1) * 2); // 创建一个只有要设置的位是1的字符
        binary.setCharAt(charIndex, (char) ((originalChar & ~mask) | bitToSet));
    }

}
